﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LPPA.Facturacion.BizEntities;
using LPPA.Facturacion.Services;
using LPPA.Facturacion.Services.Logger;

namespace LPPA.Facturacion.BusinessLayer
{
    public static class FacturaService
    {

        /// <summary>
        /// Obtiene una Factura según su Id.
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        public static FacturaBIZ GetById(int Id)
        {
            try
            {
                using (DataAccess.dcEdenorteDataContext context = new DataAccess.dcEdenorteDataContext())
                {
                    var fcCollection =
                        from fc in context.FC_Cabeceras
                        where fc.Nro_Factura == Id
                        select fc;

                    return new FacturaBIZ() { 
                                            DetalleFactura = GetDetalleByFCId(Id), 
                                            NumeroFactura = fcCollection.SingleOrDefault().Nro_Factura, 
                                            Fecha = fcCollection.SingleOrDefault().Fecha,
                                             MontoTotal = (fcCollection.SingleOrDefault().MontoTotal.HasValue) ? fcCollection.SingleOrDefault().MontoTotal.Value : 0,
                                             Titular = new TitularService().GetById(fcCollection.SingleOrDefault().Titular), 
                                             UrlFc = "/Views/Facturacion/Factura.aspx?Id="+ fcCollection.SingleOrDefault().Nro_Factura.ToString()
                                            };
                }
            }
            catch (Exception ex)
            {
                LogManager.WriteError("Error al obtener un Titular por su Id.", ex.StackTrace);
                throw;
            }
        }

        private static List<FacturaDetalle> GetDetalleByFCId(int Id)
        {
            try
            {
                using (DataAccess.dcEdenorteDataContext context = new DataAccess.dcEdenorteDataContext())
                {
                    var fcCollection =
                        from fcd in context.FC_Detalles
                        where fcd.Nro_Factura == Id
                        select fcd;

                    var listReturn = new List<FacturaDetalle>();
                    foreach (var fcdet in fcCollection)
                    {
                        listReturn.Add(new FacturaDetalle() {NroFactura = fcdet.Nro_Factura, Descripcion = fcdet.Detalle, SubTotal = fcdet.SubTotal });
                    }

                    return listReturn;
                }
            }
            catch (Exception ex)
            {
                LogManager.WriteError("Error al obtener un Titular por su Id.", ex.StackTrace);
                throw;
            }
        }

        /// <summary>
        /// Generar Factura para un Medidor.
        /// </summary>
        /// <param name="CodigoMedidor"></param>
        /// <returns></returns>
        public static FacturaBIZ FacturarMedidor(int CodigoMedidor)
        {
            try
            {
                using (DataAccess.dcEdenorteDataContext context = new DataAccess.dcEdenorteDataContext())
            {
                FacturaBIZ facturaReturn = new FacturaBIZ();
                MedidorBIZ medidor = MedidorService.GetMedidorByNumeroMedidor(CodigoMedidor);

                //Create Factura
                int nroFC = CreateFC(medidor.Titular.CodigoTitular);

                //Set Cabecera
                facturaReturn.Fecha = DateTime.Now;
                facturaReturn.Titular = medidor.Titular;
                facturaReturn.NumeroFactura = nroFC;

                //Create Detalles
                #region Mediciones Reactivas

                //Trato de obtener las ultimas dos mediciones
                var medicionesReactivas =
                    (from m in context.Mediciones
                         where m.Medidor == CodigoMedidor
                         & m.TipoCorriente.Equals(Enums.TipoCorriente.CorrienteReactiva)
                         orderby m.Fecha descending
                         select m).Take(2);

                if (medicionesReactivas.Count() > 0) // Tiene mediciones
                {
                    FacturaDetalle detalle = new FacturaDetalle();
                    facturaReturn.DetalleFactura = new List<FacturaDetalle>();

                    int consumo = 0;


                    //Calculo subtotal del consumo
                    if (medicionesReactivas.Count() == 1) // Tiene una sola medicion
                        consumo = (medicionesReactivas.ToArray()[0].Consumo - CommonsValues.MINIMO_ESPERADO_KW_CTE_REACTIVA);

                    if (medicionesReactivas.Count() == 2) // Tiene 2 mediciones
                        consumo = (medicionesReactivas.ToArray()[0].Consumo - medicionesReactivas.ToArray()[1].Consumo);


                    detalle.SubTotal = (consumo > 0) ? consumo : 0 * CommonsValues.PRECIO_KW_CTE_REACTIVA;

                    detalle.Descripcion = medicionesReactivas.ToArray()[0].Consumo.ToString() + " KW consumidos de CORRIENTE REACTIVA.";
                    detalle.NroFactura = nroFC;
                    
                    facturaReturn.DetalleFactura.Add(detalle);
                }

                #endregion

                #region Mediciones Inductivas
                if (medidor.TipoMedidor.Equals(2))
                {
                    var medicionesInductivas =
                        (from m in context.Mediciones
                         where m.Medidor == CodigoMedidor
                         & m.TipoCorriente.Equals(Enums.TipoCorriente.CorrienteInductiva)
                         orderby m.Fecha descending
                         select m).Take(2);

                    if (medicionesInductivas.Count() > 0) // Tiene mediciones
                    {
                        FacturaDetalle detalle = new FacturaDetalle();

                        int consumo = 0;

                        //Calculo subtotal del consumo
                        if (medicionesInductivas.Count() == 1) // Tiene una sola medicion
                            consumo = (medicionesInductivas.ToArray()[0].Consumo - CommonsValues.MINIMO_ESPERADO_KW_CTE_INDUCTIVA - CommonsValues.MINIMO_KW_CTE_INDUCTIVA);

                        if (medicionesInductivas.Count() == 2) // Tiene 2 mediciones
                            consumo = (medicionesInductivas.ToArray()[0].Consumo - medicionesInductivas.ToArray()[1].Consumo - CommonsValues.MINIMO_KW_CTE_INDUCTIVA);

                        detalle.SubTotal = (consumo > 0) ? consumo : 0 * CommonsValues.PRECIO_KW_CTE_INDUCTIVA;
                        detalle.Descripcion = "NRO Medidor: " + medidor.NumeroMedidor.ToString() + " | " + medicionesInductivas.ToArray()[0].Consumo.ToString() + " KW consumidos de CORRIENTE INDUCTIVA.";
                        detalle.NroFactura = nroFC;

                        facturaReturn.DetalleFactura.Add(detalle);
                    }
                
                }
                #endregion


                //Calculate Monto Total e Insert Detalles
                int montoTotal = 0;
                if (facturaReturn.DetalleFactura != null)
                {
                    foreach (FacturaDetalle fcDetalle in facturaReturn.DetalleFactura)
                    {
                        montoTotal = montoTotal + fcDetalle.SubTotal;
                        InsertDetalle(fcDetalle);
                    }
                    facturaReturn.MontoTotal = montoTotal;
                }
                //Update Monto FC
                UpdateMontoFC(facturaReturn);
                
                //Url FC
                facturaReturn.UrlFc = "/Views/Facturacion/Factura.aspx?Id=" + facturaReturn.NumeroFactura;

                return facturaReturn;
            }
            }
            catch (Exception ex)
            {
                LogManager.WriteError("Error al intentar generar la Facutura del Medidor.", ex.StackTrace);
                throw;
            }
        }

        /// <summary>
        /// Generar Facturas de todos los Medidores existentes.
        /// </summary>
        /// <returns></returns>
        public static List<FacturaBIZ> FacturarAll()
        {
            try
            {
                using (DataAccess.dcEdenorteDataContext context = new DataAccess.dcEdenorteDataContext())
            {
                List<FacturaBIZ> _ListToReturn = new List<FacturaBIZ>();

                foreach (var medidor in MedidorService.GetAll())
                {
                    _ListToReturn.Add(FacturaService.FacturarMedidor(medidor.NumeroMedidor));
                }
                
                return _ListToReturn;
            }
            }
            catch (Exception ex)
            {
                LogManager.WriteError("Error al intentar generar las Facuturas de todos los medidores.", ex.StackTrace);
                throw;
            }
        }

        /// <summary>
        /// Generar Facturas de todos los Medidores según calle.
        /// </summary>
        /// <param name="Calle"></param>
        /// <returns></returns>
        public static List<FacturaBIZ> FacturarAllByCalle(string Calle)
        {
            try
            {
                using (DataAccess.dcEdenorteDataContext context = new DataAccess.dcEdenorteDataContext())
            {
                List<FacturaBIZ> _ListToReturn = new List<FacturaBIZ>();

                var medidores = MedidorService.GetAllByCalle(Calle);

                foreach (var medidor in medidores)
                {
                    _ListToReturn.Add(FacturaService.FacturarMedidor(medidor.NumeroMedidor));
                }

                return _ListToReturn;
            }
            }
            catch (Exception ex)
            {
                LogManager.WriteError("Error al intentar generar las Facuturas de todos los medidores instalados en una calle.", ex.StackTrace);
                throw;
            }
        }

        /// <summary>
        /// Generar Facturas de todos los Medidores según calle y rango de alturas.
        /// </summary>
        /// <param name="Calle"></param>
        /// <param name="AlturaInicial"></param>
        /// <param name="AlturaFinal"></param>
        /// <returns></returns>
        public static List<FacturaBIZ> FacturarAllByCalleRangoAlturas(string Calle, int AlturaInicial, int AlturaFinal)
        {
            try
            {
                 using (DataAccess.dcEdenorteDataContext context = new DataAccess.dcEdenorteDataContext())
            {
                List<FacturaBIZ> _ListToReturn = new List<FacturaBIZ>();

                var medidores = MedidorService.GetAllByCalleRangoAlturas(Calle, AlturaInicial, AlturaFinal);

                foreach (var medidor in medidores)
                {
                    _ListToReturn.Add(FacturaService.FacturarMedidor(medidor.NumeroMedidor));
                }

                return _ListToReturn;
            }
            }
            catch (Exception ex)
            {
                LogManager.WriteError("Error al intentar generar las Facuturas de todos los medidores instalados en una calle y rango de alturas.", ex.StackTrace);
                throw;
            }
        }
        
        #region Private Methods

        /// <summary>
        /// Update Monto Total de FC en proceso.
        /// </summary>
        /// <param name="facturaReturn"></param>
        public static void UpdateMontoFC(FacturaBIZ facturaReturn)
        {
            try
            {
                using (DataAccess.dcEdenorteDataContext context = new DataAccess.dcEdenorteDataContext())
            {
                var facturaToUpdate =
                    (from f in context.FC_Cabeceras
                    where f.Nro_Factura == facturaReturn.NumeroFactura
                    select f).SingleOrDefault();

                facturaToUpdate.MontoTotal = facturaReturn.MontoTotal;

                context.SubmitChanges();
            }
            }
            catch (Exception ex)
            {
                LogManager.WriteError("Error al actualizar el Monto de una Factura", ex.StackTrace);
                throw;
            }
            
        }

        /// <summary>
        /// Insertar Detalle en Factura
        /// </summary>
        /// <param name="FCDetalle"></param>
        public static void InsertDetalle(FacturaDetalle FCDetalle)
        {
            try
            {
                using (DataAccess.dcEdenorteDataContext context = new DataAccess.dcEdenorteDataContext())
            {
                context.FC_Detalles.InsertOnSubmit(new DataAccess.FC_Detalle()
                {
                    Detalle = FCDetalle.Descripcion.ToString(),
                    Nro_Factura = FCDetalle.NroFactura,
                    SubTotal = FCDetalle.SubTotal
                });
                context.SubmitChanges();
            }
            }
            catch (Exception ex)
            {
                LogManager.WriteError("Error al insertar el detalle de una factura.", ex.StackTrace);
                throw;
            }
        }

        /// <summary>
        /// Crear Factura (Cabecera) para un Titular.
        /// </summary>
        /// <param name="CodigoTitular"></param>
        /// <returns></returns>
        public static int CreateFC(int CodigoTitular)
        {
            try
            {
                using (DataAccess.dcEdenorteDataContext context = new DataAccess.dcEdenorteDataContext())
            {
                var fcCabecera = new DataAccess.FC_Cabecera() { Fecha = DateTime.Now, Titular = CodigoTitular};
                context.FC_Cabeceras.InsertOnSubmit(fcCabecera);
                context.SubmitChanges();

                return fcCabecera.Nro_Factura;
            }
            }
            catch (Exception ex)
            {
                LogManager.WriteError("Error al intentar crear una Factura para un Titular determinado.", ex.StackTrace);
                throw;
            }
        }
        #endregion
    }
}
